/*
 * Copyright 2008 Konstantin Triger <kostat@gmail.com> 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *  http://www.apache.org/licenses/LICENSE-2.0 
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package jaque.functions;

/**
 * Implements a comparator predicate.
 * 
 * @param <T>
 *            the type of the objects to compare.
 * @param <Key>
 *            the type of the key by which the comparison is performed.
 * 
 * @author <a href="mailto://object_streaming@googlegroups.com">Konstantin
 *         Triger</a>
 */
public final class Comparator<T, Key> // extends Comparable<? super Key>
		implements Predicate<T>, Function2<Boolean, T, T> {

	private static final java.util.Comparator<Object> _default = new ComparatorImplementation<Object>();
	private static final java.util.Comparator<? extends Comparable<Object>> _defaultComparable = new ComparableComparatorImplementation<Comparable<Object>>();

	/**
	 * Returns a default comparator which uses {@code Object.equals() }.
	 * 
	 * @param <Key>
	 *            the type of the objects to compare.
	 * @return a default comparator which uses {@code Object.equals() }.
	 */
	@SuppressWarnings("unchecked")
	public static <Key> java.util.Comparator<Key> getDefault() {
		return (java.util.Comparator<Key>) _default;
	}

	/**
	 * Returns a default comparator which uses {@code Comparable.compareTo() }.
	 * 
	 * @param <Key>
	 *            the type of the objects to compare.
	 * @return a default comparator which uses {@code Comparable.compareTo() }.
	 */
	@SuppressWarnings("unchecked")
	public static <Key extends Comparable<? super Key>> java.util.Comparator<Key> getComparableDefault() {
		return (java.util.Comparator<Key>) _defaultComparable;
	}

	private static final class ComparatorImplementation<T> implements
			java.util.Comparator<T> {
		// @Override
		public int compare(T left, T right) {
			return left == null ? (right == null ? 0 : -1) : (left
					.equals(right) ? 0 : 1);
		}
	}

	private static final class ComparableComparatorImplementation<T extends Comparable<? super T>>
			implements java.util.Comparator<T> {
		// @Override
		public int compare(T left, T right) {
			return left.compareTo(right);
		}
	}

	public enum Operator {
		/**
		 * &lt; comparison operator.
		 */
		LessThan {
			boolean eval(int result) {
				return result < 0;
			}
		},
		/**
		 * &lt;= comparison operator.
		 */
		LessThanOrEqual {
			boolean eval(int result) {
				return result <= 0;
			}
		},
		/**
		 * = comparison operator.
		 */
		Equal {
			boolean eval(int result) {
				return result == 0;
			}
		},
		/**
		 * &gt;= comparison operator.
		 */
		GreaterThanOrEqual {
			boolean eval(int result) {
				return result >= 0;
			}
		},
		/**
		 * &gt; comparison operator.
		 */
		GreaterThan {
			boolean eval(int result) {
				return result > 0;
			}
		};

		abstract boolean eval(int result);
	}

	private final Function<? extends Key, ? super T> _left;
	private final Function<? extends Key, ? super T> _right;
	private final java.util.Comparator<? super Key> _comparator;
	private final Operator _op;

	// public <Key1 extends Comparable<? super Key>> Comparator(Function<? super
	// T, ? extends Key1> left,
	// Function<? super T, ? extends Key1> right, Operator op) {
	// this(left, right, op, null);
	// // _right = right;
	// // _op = op;
	// }

	/**
	 * Constructs the Comparator predicate.
	 * 
	 * @param left
	 *            a function selecting left side of comparison.
	 * @param right
	 *            a function selecting right side of comparison.
	 * @param op
	 *            comparison operator.
	 * @param comparator
	 *            a comparator to be used.
	 */
	public Comparator(Function<? extends Key, ? super T> left,
			Function<? extends Key, ? super T> right, Operator op,
			final java.util.Comparator<? super Key> comparator) {
		_left = left;
		_right = right;
		_op = op;
		_comparator = comparator;
	}

	// @Override
	public Boolean invoke(T source) throws Throwable {
		return invoke(source, source);
	}

	// @Override
	public Boolean invoke(T source1, T source2) throws Throwable {
		return _op.eval(_comparator.compare(_left.invoke(source1), _right
				.invoke(source2)));
	}
}
